#ifndef _MATHEMATICS_ALGEBRA_SET_
#define _MATHEMATICS_ALGEBRA_SET_

#include <vector>
#include <algorithm>
#include <stdexcept>

namespace science
{
	namespace mathematics
	{
		namespace algebra
		{
			template <typename T>
			class set
			{
				typedef std::vector<T> set_elements;
				typedef typename set_elements::iterator set_elements_iterator;
				typedef typename set_elements::const_iterator set_elements_const_iterator;

			private:
				set_elements _elements;

			public:
				set()
				{
					_elements.empty();
				}

				set(set_elements elements)
				{
					for (const auto& v : elements)
						_elements.push_back(v);
				}

				~set() {}

			public:
				void add_element(const T& element)
				{
					_elements.push_back(element);
				}

				void add_elements(const set<T>& elements)
				{
					for (const auto& v : elements._elements)
						_elements.push_back(v);
				}

				int find_element(const T& element) const
				{
					auto found = std::find(_elements.begin(), _elements.end(), element);
					if (found != _elements.end())
						return std::distance(_elements.begin(), found);
					return -1;
				}

				void remove_element(const T& element)
				{
					int found = find_element(element);
					if (found != -1)
						_elements.erase(_elements.begin() + found);
				}

				void remove_at(unsigned int index)
				{
					if (index >= _elements.size())
						throw new std::invalid_argument("Invalid elemenet index");
					_elements.erase(_elements.begin() + index);
				}

				T get_elemenet(unsigned int index) const
				{
					if (index >= _elements.size())
						throw new std::invalid_argument("Invalid elemenet index");
					return _elements[index];
				}

				void set_element(unsigned int index, const T& val)
				{
					if (index >= _elements.size())
						throw new std::invalid_argument("Invalid elemenet index");
					_elements[index] = val;
				}

				void remove_all()
				{
					_elements.clear();
				}

				unsigned int size() const { return _elements.size(); }

			public:
				static set<T> set_union(const set<T>& set1, const set<T>& set2)
				{
					set<T> result;
					result.add_elements(set1);

					for (const auto& v : set2)
					{
						if (set1.find_element(v) == -1)
							result.add_element(v);
					}

					return result;
				}

				static set<T> set_intersect(const set<T>& set1, const set<T>& set2)
				{
					set<T> result;
					for (const auto& v : set1)
					{
						if (set2.find_element(v) != -1 && result.find_element(v) == -1)
							result.add_element(v);
					}

					return result;
				}

			public:
				set_elements_iterator begin() { return _elements.begin(); }
				set_elements_iterator end() { return _elements.end(); }
				set_elements_const_iterator begin() const { return _elements.begin(); }
				set_elements_const_iterator end() const { return _elements.end(); }
			};
		}
	}
}

#endif